#include "PageCache.h"

PageCache PageCache::_Inst;
std::recursive_mutex PageCache::_mtx;
// std::unordered_map<PageId_t, Span *> PageCache::_idSpanMap;
std::map<PageId_t, Span *> PageCache::_idSpanMap;

ObjectPool<Span> PageCache::SpanPool;

Span *PageCache::NewSpan(size_t k)
{
    assert(k >= 1);
    if (k >= NPAGES)
    {
// 页桶无法给，直接向堆申请空间
#ifdef _WIN64
        void *ptr = VirtualAlloc(0, k * (1 << ShiftPageSize), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#elif _WIN32
        void *ptr = VirtualAlloc(0, (1 << ShiftPageSize), MEM_RESERVE, PAGE_READWRITE);
// #elif Linux
#endif
        Span *span = SpanPool.New();
        span->_PageId = (PageId_t)ptr >> ShiftPageSize;
        _idSpanMap[span->_PageId] = span;
        span->_n = NPAGES;
        return span;
    }
    // 1.刚好k号桶就有span
    if (!_spanLists[k].IsEmpty())
    {
        return _spanLists[k].PopFront();
    }

    // 2.k号桶没有,但是比k大的桶有切
    for (int i = k + 1; i < NPAGES; i++)
    {
        if (!_spanLists[i].IsEmpty())
        {
            Span *nSpan = _spanLists[i].PopFront();

            // Span *kSpan = new Span();
            Span *kSpan = SpanPool.New();

            kSpan->_n = k;
            kSpan->_PageId = nSpan->_PageId;

            nSpan->_n = nSpan->_n - k;
            nSpan->_PageId = nSpan->_PageId + k;

            _spanLists[nSpan->_n].PushFront(nSpan);
            // _spanLists[kSpan->_n].PushFront(kSpan);

            _idSpanMap[nSpan->_PageId] = nSpan;
            _idSpanMap[nSpan->_PageId + nSpan->_n - 1] = nSpan;

            // 所有碎片映射方便CentralCache回收碎片
            for (int PageId = 0; PageId < kSpan->_n; PageId++)
            {
                _idSpanMap[kSpan->_PageId + PageId] = kSpan;
            }
            return kSpan;
        }
    }

// 3.没有大的桶切分，直接向对申请内存
#ifdef _WIN64
    void *ptr = VirtualAlloc(0, (NPAGES - 1) * (1 << ShiftPageSize), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#elif _WIN32
    void *ptr = VirtualAlloc(0, (1 << ShiftPageSize), MEM_RESERVE, PAGE_READWRITE);
// #elif Linux
#endif

    Span *MaxSpan = new Span();
    MaxSpan->_freeList = ptr;
    MaxSpan->_n = (NPAGES - 1);
    MaxSpan->_PageId = ((PageId_t)MaxSpan->_freeList) >> ShiftPageSize;

    _spanLists[NPAGES - 1].PushFront(MaxSpan);

    return PageCache::NewSpan(k);
}
Span *PageCache::MapObjToSpan(void *ptr)
{
    assert(ptr);
    PageId_t pageId = ((PageId_t)ptr >> ShiftPageSize);
    auto it = _idSpanMap.find(pageId);
    if (it != _idSpanMap.end())
    {
        return it->second;
    }
    else
    {
        assert(false);
        return nullptr;
    }
}
void PageCache::SpnaGiveBackPage(Span *span)
{
    // 空间是直接堆申请
    if (span->_n == NPAGES)
    {
#ifdef _WIN64
    VirtualFree((void*)(span->_PageId >> ShiftPageSize),span->_size,MEM_DECOMMIT);
    return;
#elif _WIN32
    VirtualFree(span->_PageId >> ShiftPageSize,span->_size,MEM_DECOMMIT);
    return;
// #elif Linux
#endif
    }
    // 往pageId后面找是否有连续的空间可以合并
    while (true)
    {
        auto it = _idSpanMap.find(span->_PageId + span->_n);
        if (it == _idSpanMap.end())
        {
            break;
        }
        Span *nextSpan = it->second;
        if (nextSpan->_isUse == true)
        {
            break;
        }
        if (nextSpan->_n + span->_n > NPAGES - 1)
        {
            break;
        }
        // 可以合并
        span->_n += nextSpan->_n;

        // 删除小的span
        // _idSpanMap.erase(nextSpan->_PageId);
        _spanLists[nextSpan->_n].Erase(nextSpan);

        SpanPool.Delete(nextSpan);
    }
    // 往pageId前面找是否有连续的空间可以合并
    while (true)
    {
        auto it = _idSpanMap.find(span->_PageId + span->_n);
        if (it == _idSpanMap.end())
        {
            break;
        }
        Span *frontSpan = it->second;
        if (frontSpan->_isUse == true)
        {
            break;
        }
        if (frontSpan->_n + span->_n > NPAGES - 1)
        {
            break;
        }
        // 可以合并
        span->_n += frontSpan->_n;
        span->_PageId = frontSpan->_PageId;

        // 删除小的span
        // _idSpanMap.erase(frontSpan->_PageId);
        _spanLists[frontSpan->_n].Erase(frontSpan);
        SpanPool.Delete(frontSpan);
    }

    _spanLists[span->_n].PushFront(span);
    _idSpanMap[span->_PageId] = span;
    _idSpanMap[span->_PageId + span->_n - 1] = span;
    span->_isUse = false;
}
